﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using MahApps.Metro.Controls;
using NPOI.HSSF.Record;
using Xceed.Wpf.Toolkit;

namespace LunarSF.SHomeWorkshop.LunarMarkdownEditor
{
    /// <summary>
    /// QuestionEditor.xaml 的交互逻辑
    /// </summary>
    public partial class QuestionEditor : MetroWindow
    {
        public QuestionEditor(List<QuestionInfo> questionInfos, MarkdownEditor masterEditor)
        {
            InitializeComponent();

            this.masterEditor = masterEditor;
            this.Title = Globals.AppName + " - 选择题编辑器";

            this.questionInfos = questionInfos;
            mtbNumber.Minimum = 1;
            mtbNumber.Maximum = questionInfos.Count;
            tbCount.Text = $"共 {questionInfos.Count.ToString()} 题";

            qEdits.Add(new QEdit() { ChoiceItemEdit = atbAnswer, ChoiceItemAnalyEdit = atbAnswerAnaly, ChoiceItemCheckPointEdit = tbCheckPointOfAnswer, });
            qEdits.Add(new QEdit() { ChoiceItemEdit = atbError1, ChoiceItemAnalyEdit = atbError1Analy, ChoiceItemCheckPointEdit = tbCheckPointOfBranch1, });
            qEdits.Add(new QEdit() { ChoiceItemEdit = atbError2, ChoiceItemAnalyEdit = atbError2Analy, ChoiceItemCheckPointEdit = tbCheckPointOfBranch2, });
            qEdits.Add(new QEdit() { ChoiceItemEdit = atbError3, ChoiceItemAnalyEdit = atbError3Analy, ChoiceItemCheckPointEdit = tbCheckPointOfBranch3, });

            anaTbxs.Add(atbAnswerAnaly);
            anaTbxs.Add(atbError1Analy);
            anaTbxs.Add(atbError2Analy);
            anaTbxs.Add(atbError3Analy);

            // 移除所有内置的快捷菜单——没用还丑陋
            atbTitle.ContextMenu = null;
            foreach (var qedit in qEdits)
            {
                qedit.ChoiceItemEdit.ContextMenu = null;
                qedit.ChoiceItemAnalyEdit.ContextMenu = null;
                qedit.ChoiceItemCheckPointEdit.ContextMenu = null;
            }

            Loaded += QuestionEditor_Loaded;

            var cm = new ContextMenu();
            TextOptions.SetTextFormattingMode(cm, TextFormattingMode.Display);
            cm.FontFamily = new FontFamily("simsun");

            var miExpandSelection = new MenuItem()
            {
                Header = "扩选(_E)",
                Style = TryFindResource("MetroMenuItem") as Style,
            };
            miExpandSelection.Click += MiExpandSelection_Click;
            cm.Items.Add(miExpandSelection);
            cm.Items.Add(new Separator());

            var miGotoErrorItem01 = new MenuItem()
            {
                Header = "到错项 _1 解析",
                Style = TryFindResource("MetroMenuItem") as Style,
            };
            miGotoErrorItem01.Click += MiGotoErrorItem01_Click;
            cm.Items.Add(miGotoErrorItem01);

            var miGotoErrorItem02 = new MenuItem()
            {
                Header = "到错项 _2 解析",
                Style = TryFindResource("MetroMenuItem") as Style,
            };
            miGotoErrorItem02.Click += MiGotoErrorItem02_Click;
            cm.Items.Add(miGotoErrorItem02);

            var miGotoErrorItem03 = new MenuItem()
            {
                Header = "到错项 _3 解析",
                Style = TryFindResource("MetroMenuItem") as Style,
            };
            miGotoErrorItem03.Click += MiGotoErrorItem03_Click;
            cm.Items.Add(miGotoErrorItem03);

            cm.Items.Add(new Separator());

            var miDeleteSelectedText = new MenuItem()
            {
                Header = "删除(_D)",
                Style = TryFindResource("MetroMenuItem") as Style,
            };
            miDeleteSelectedText.Click += MiDeleteSelectedText_Click;
            cm.Items.Add(miDeleteSelectedText);

            var miDeleteToStart = new MenuItem()
            {
                Header = "删除到行首(_H)",
                Style = TryFindResource("MetroMenuItem") as Style,
                ToolTip = "按 Shift 和【[*]删除到行首】效果相同。",
            };
            miDeleteToStart.Click += MiDeleteToStart_Click;
            cm.Items.Add(miDeleteToStart);

            var miDeleteToEnd = new MenuItem()
            {
                Header = "删除到行尾(_T)",
                Style = TryFindResource("MetroMenuItem") as Style,
                ToolTip = "按 Shift 和【[*]删除到行尾】效果相同。",
            };
            miDeleteToEnd.Click += MiDeleteToEnd_Click;
            cm.Items.Add(miDeleteToEnd);

            cm.Items.Add(new Separator());

            var miDeleteToStartWithSelectedText = new MenuItem()
            {
                Header = "[*]删除到行首(_H)",
                Style = TryFindResource("MetroMenuItem") as Style,
                ToolTip = "连选中文本一起删除。",
            };
            miDeleteToStartWithSelectedText.Click += MiDeleteToStartWithSelectedText_Click;
            cm.Items.Add(miDeleteToStartWithSelectedText);

            var miDeleteToEndWithSelectedText = new MenuItem()
            {
                Header = "[*]删除到行尾(_T)",
                Style = TryFindResource("MetroMenuItem") as Style,
                ToolTip = "连选中文本一起删除。",
            };
            miDeleteToEndWithSelectedText.Click += MiDeleteToEndWithSelectedText_Click;
            cm.Items.Add(miDeleteToEndWithSelectedText);

            atbAnswerAnaly.Tag = cm;

            cm.PreviewKeyDown += Cm_PreviewKeyDown;

            this.Closing += QuestionEditor_Closing;

            var cprvText = App.WorkspaceConfigManager.Get("CheckPointPanelRowsVisible");
            if (string.IsNullOrWhiteSpace(cprvText) == false)
            {
                bool cprv;
                if (bool.TryParse(cprvText, out cprv))
                {
                    this.isCheckPointPanelRowsVisible = cprv;
                }
            }
            RefreshIsCheckPointPanelRowsVisible();

        }

        private void Cm_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            switch (e.Key)
            {
                case Key.Back:
                case Key.Delete:
                    {
                        MiDeleteSelectedText_Click(sender, e);
                        (sender as ContextMenu).IsOpen = false;
                        e.Handled = true;
                        break;
                    }
                case Key.D:
                    {
                        KeyStates ksLCtrl = Keyboard.GetKeyStates(Key.LeftCtrl);
                        KeyStates ksRCtrl = Keyboard.GetKeyStates(Key.RightCtrl);

                        bool isCtrl = (ksLCtrl & KeyStates.Down) > 0 || (ksRCtrl & KeyStates.Down) > 0;
                        if (isCtrl)
                        {
                            (sender as ContextMenu).IsOpen = false;
                            e.Handled = true;
                        }
                        break;
                    }
            }
        }

        private MarkdownEditor masterEditor = null;

        private void MiExpandSelection_Click(object sender, RoutedEventArgs e)
        {
            foreach (var tbx in anaTbxs)
            {
                if (tbx.IsFocused)
                {
                    var rtxt = tbx.Text.Substring(tbx.SelectionStart + tbx.SelectionLength);
                    var nextMarkIndex = -1;
                    for (int i = 0; i < rtxt.Length; i++)
                    {
                        var c = rtxt[i];
                        if (";；.。".Contains(c))
                        {
                            nextMarkIndex = i;
                            break;
                        }
                    }

                    var tmpLength = tbx.SelectionLength + nextMarkIndex + 1;
                    if (nextMarkIndex >= 0 && tmpLength >= 0)
                    {
                        tbx.Select(tbx.SelectionStart, tmpLength);
                    }
                    else
                    {
                        //选中到行尾
                        tmpLength = tbx.Text.Length - tbx.SelectionStart;
                        if (tmpLength >= 0)
                        {
                            tbx.Select(tbx.SelectionStart, tmpLength);
                        }
                    }
                    var cm = tbx.Tag as ContextMenu;
                    if (cm != null)
                        cm.IsOpen = true;
                    return;
                }
            }
        }

        private void MiDeleteToEndWithSelectedText_Click(object sender, RoutedEventArgs e)
        {
            var tbx = atbAnswerAnaly;
            var len = tbx.SelectionLength;
            var newStart = tbx.SelectionStart + len;

            // 这个效果和按 Shift + Mi6 一样。
            tbx.Select(tbx.SelectionStart, tbx.Text.Length - tbx.SelectionStart);

            tbx.SelectedText = "";  // 删除到行尾不需要再选中什么了。这题基本结束。
            return;
        }

        private void MiDeleteToStartWithSelectedText_Click(object sender, RoutedEventArgs e)
        {
            var tbx = atbAnswerAnaly;
            var len = tbx.SelectionLength;

            tbx.Select(0, tbx.SelectionStart + len);
            // 这个效果和按 Shift + Mi5 一样。

            tbx.SelectedText = "";
            //删除到行首一般需要选中下一句。
            var rtxt = tbx.Text.Substring(tbx.SelectionStart);
            var end = -1;
            for (int i = 0; i < rtxt.Length; i++)
            {
                var c = rtxt[i];
                if (";；.。".Contains(c))
                {
                    end = i;
                    break;
                }
            }

            if (end >= 0)
            {
                tbx.Select(tbx.SelectionStart, end + 1);
            }
            else
            {
                tbx.Select(0, len);
            }
            return;
        }

        private void MiDeleteToEnd_Click(object sender, RoutedEventArgs e)
        {
            var tbx = atbAnswerAnaly;
            var len = tbx.SelectionLength;
            var newStart = tbx.SelectionStart + len;

            KeyStates ksRShift = Keyboard.GetKeyStates(Key.RightShift);
            KeyStates ksLShift = Keyboard.GetKeyStates(Key.LeftShift);
            bool isShift = (ksLShift & KeyStates.Down) > 0 || (ksRShift & KeyStates.Down) > 0;

            if (isShift)
            {
                tbx.Select(tbx.SelectionStart, tbx.Text.Length - tbx.SelectionStart);
                // 按 Shift 连选中文本一起删除。
            }
            else
            {
                tbx.Select(newStart, tbx.Text.Length - newStart);
            }

            tbx.SelectedText = "";  // 删除到行尾不需要再选中什么了。这题基本结束。
            return;
        }

        private void MiDeleteToStart_Click(object sender, RoutedEventArgs e)
        {
            var tbx = atbAnswerAnaly;
            var len = tbx.SelectionLength;

            KeyStates ksRShift = Keyboard.GetKeyStates(Key.RightShift);
            KeyStates ksLShift = Keyboard.GetKeyStates(Key.LeftShift);
            bool isShift = (ksLShift & KeyStates.Down) > 0 || (ksRShift & KeyStates.Down) > 0;

            if (isShift)
            {
                tbx.Select(0, tbx.SelectionStart + len);
                // 按 Shift 时连自身一并删除。
            }
            else
            {
                tbx.Select(0, tbx.SelectionStart);
            }

            tbx.SelectedText = "";
            //删除到行首一般需要选中下一句。
            var rtxt = tbx.Text.Substring(tbx.SelectionStart);
            var end = -1;
            for (int i = 0; i < rtxt.Length; i++)
            {
                var c = rtxt[i];
                if (";；.。".Contains(c))
                {
                    end = i;
                    break;
                }
            }

            if (end >= 0)
            {
                tbx.Select(tbx.SelectionStart, end + 1);
            }
            else
            {
                tbx.Select(0, len);
            }
            return;
        }

        private void MiDeleteSelectedText_Click(object sender, RoutedEventArgs e)
        {
            var tbx = atbAnswerAnaly;
            tbx.SelectedText = "";
            if (tbx.SelectionStart > 0)
            {
                var left = tbx.Text.Substring(0, tbx.SelectionStart).TrimEnd(new char[] { ',', '.', ';', '；', ' ', '　', '\t', });
                if (left.Length < tbx.SelectionStart)
                {
                    tbx.Select(left.Length, tbx.SelectionStart - left.Length);
                    tbx.SelectedText = "。";
                    tbx.Select(tbx.SelectionStart + 1, 0);
                }
            }
            return;
        }

        private void QuestionEditor_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (this.DialogResult.HasValue == false || this.DialogResult.Value == false)
            {
                var result = LMessageBox.Show("要将当前编辑的内容同步到主编辑器中吗？", Globals.AppName,
                    MessageBoxButton.YesNoCancel, MessageBoxImage.Question, "", null, this);
                if (result == MessageBoxResult.Yes)
                {
                    UpdateQuestion(mtbNumber.Value.Value);
                    // 先暂存数据，准备同步
                }
                else if (result == MessageBoxResult.Cancel)
                {
                    e.Cancel = true;  // 返回继续编辑。
                    return;
                }
                else if (result == MessageBoxResult.No)
                {
                    // 放弃数据，直接关闭窗口。
                    return;
                }
            }
            else
            {
                if (this.DialogResult.Value == true)
                {
                    UpdateQuestion(mtbNumber.Value.Value);
                    // 按下了 Submit 按钮，先暂存数据再关闭。
                }
                // 如果是按下了 Cancel 按钮，直接关闭。
            }
        }

        private void MiGotoErrorItem03_Click(object sender, RoutedEventArgs e)
        {
            btnToError3Analy_Click(sender, e);
        }

        private void MiGotoErrorItem02_Click(object sender, RoutedEventArgs e)
        {
            btnToError2Analy_Click(sender, e);
        }

        private void MiGotoErrorItem01_Click(object sender, RoutedEventArgs e)
        {
            btnToError1Analy_Click(sender, e);
        }

        private void QuestionEditor_Loaded(object sender, RoutedEventArgs e)
        {
            mtbNumber.Value = 1;
        }

        private List<QEdit> qEdits = new List<QEdit>();

        private List<QuestionInfo> questionInfos;
        /// <summary>
        /// 关闭窗口后，要从此属性获取修改后的数据。
        /// </summary>
        public List<QuestionInfo> QuestionInfos { get { return questionInfos; } }

        private string markdownImageLinkText = null;

        private void ShowQuestion()
        {
            if (mtbNumber.Value.HasValue == false)
            {
                LMessageBox.Show("请指定要显示哪一题。", Globals.AppName, MessageBoxButton.OK,
                    MessageBoxImage.Warning, "", "", this);
                return;
            }

            if (ipw != null)
            {
                ipw.Close();
            }

            var index = mtbNumber.Value.Value - 1;
            if (index < 0 || index >= questionInfos.Count) return;

            var qi = questionInfos[index];

            if (qi == null || qi.ChoiceItems.Count != qEdits.Count)
            {
                LMessageBox.ShowWarning("题支数量不是 4 个。不支持。");
                return;
            }

            if (string.IsNullOrEmpty(qi.MarkdownImagePath) == false)
            {
                qi.MarkdownImageLinkRealPath = masterEditor?.GetRealPathFromRelativeImagePath(qi.MarkdownImagePath);
            }

            var trimchars = new char[] { '\r', '\n', ' ', '　', '\t', };
            var titleText = qi.TitleString.Trim(trimchars);
            var regNumber = new Regex(@"^[\(（]\.\d{0,}\.[\)）]");
            var matchNumber = regNumber.Match(titleText);
            if (matchNumber.Success)
            {
                atbTitle.Text = titleText.Substring(matchNumber.Length);
            }
            else
            {
                atbTitle.Text = titleText;
            }

            markdownImageLinkText = qi.MarkdownLinkText;

            for (int i = 0; i < qi.ChoiceItems.Count; i++)
            {
                var ci = qi.ChoiceItems[i];
                if (ci.IsAnswer)
                {
                    qEdits[0].ChoiceItemEdit.Text = ci.Text.Trim(trimchars);

                    qEdits[0].ChoiceItemAnalyEdit.Text = ci.AnalysisText.Trim(trimchars);

                    qEdits[0].ChoiceItemCheckPointEdit.Text = ci.CheckPoints.Trim(trimchars);
                }
            }

            int errorItemCount = 0;
            foreach (ChoiceItem ci in qi.ChoiceItems)
            {
                if (ci.IsAnswer == false)
                {
                    qEdits[errorItemCount + 1].ChoiceItemEdit.Text = ci.Text.Trim(trimchars);

                    qEdits[errorItemCount + 1].ChoiceItemAnalyEdit.Text = ci.AnalysisText.Trim(trimchars);

                    qEdits[errorItemCount + 1].ChoiceItemCheckPointEdit.Text = ci.CheckPoints.Trim(trimchars);

                    errorItemCount++;
                }
            }

            tbNumber.Text = $"{mtbNumber.Value.Value.ToString()}．";

            if (System.IO.File.Exists(qi.MarkdownImageLinkRealPath))
            {
                btnShowImage.Visibility = Visibility.Visible;
                btnShowImage.Tag = qi.MarkdownImageLinkRealPath;
            }
            else
            {
                btnShowImage.Visibility = Visibility.Hidden;
                btnShowImage.Tag = null;
            }

            atbAnswer.SelectAll();
            atbAnswerAnaly.Focus();

            if (qi.ShouldRemove)
            {
                tbShouldDelete.Text = "本题将被删除！！！";
                atbTitle.TextDecorations = TextDecorations.Strikethrough;
            }
            else
            {
                tbShouldDelete.Text = "";
                atbTitle.TextDecorations = null;
            }
        }

        private void mtbNumber_ValueChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            try
            {
                if (e.OldValue != null)
                {
                    int oldNum = (int)e.OldValue;
                    UpdateQuestion(oldNum);
                }
            }
            catch (Exception ex)
            {
                LMessageBox.Show(ex.Message);
            }

            ShowQuestion();
        }

        private void UpdateQuestion(int num)
        {
            if (num >= 1 && num <= questionInfos.Count)
            {
                var index = num - 1;
                var qi = questionInfos[index];
                qi.TitleString = $"{atbTitle.Text}\r\n\r\n{markdownImageLinkText}\r\n\r\n";
                for (int i = 0; i <= 3; i++)
                {
                    qi.ChoiceItems[i].IsAnswer = (qEdits[i].ChoiceItemEdit == atbAnswer);
                    qi.ChoiceItems[i].Text = qEdits[i].ChoiceItemEdit.Text;
                    qi.ChoiceItems[i].AnalysisText = qEdits[i].ChoiceItemAnalyEdit.Text;
                    qi.ChoiceItems[i].CheckPoints = qEdits[i].ChoiceItemCheckPointEdit.Text;
                }
            }
        }

        private void btnFst_Click(object sender, RoutedEventArgs e)
        {
            if (questionInfos.Count <= 0) return;
            var destNum = 1;
            if (destNum < 1 || destNum > questionInfos.Count) return;

            mtbNumber.Value = destNum;
        }

        private void btnPre_Click(object sender, RoutedEventArgs e)
        {
            if (questionInfos.Count <= 0) return;
            var destNum = mtbNumber.Value - 1;
            if (destNum < 1 || destNum > questionInfos.Count) return;

            mtbNumber.Value = destNum;
        }

        private void btnNxt_Click(object sender, RoutedEventArgs e)
        {
            if (questionInfos.Count <= 0) return;
            var destNum = mtbNumber.Value + 1;
            if (destNum < 1 || destNum > questionInfos.Count) return;

            mtbNumber.Value = destNum;
        }

        private void btnLst_Click(object sender, RoutedEventArgs e)
        {
            if (questionInfos.Count <= 0) return;
            var destNum = questionInfos.Count;
            if (destNum < 1 || destNum > questionInfos.Count) return;

            mtbNumber.Value = destNum;
        }

        private void BtnOk_Click(object sender, RoutedEventArgs e)
        {
            if (mtbNumber.Value.HasValue == false) return;
            this.DialogResult = true;
            this.Close();
        }

        private void BtnCancel_Click(object sender, RoutedEventArgs e)
        {
            this.DialogResult = false;
            this.Close();
        }

        private void MetroWindow_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            KeyStates ksRShift = Keyboard.GetKeyStates(Key.RightShift);
            KeyStates ksLShift = Keyboard.GetKeyStates(Key.LeftShift);
            KeyStates ksLAlt = Keyboard.GetKeyStates(Key.LeftAlt);
            KeyStates ksRAlt = Keyboard.GetKeyStates(Key.RightAlt);
            KeyStates ksLCtrl = Keyboard.GetKeyStates(Key.LeftCtrl);
            KeyStates ksRCtrl = Keyboard.GetKeyStates(Key.RightCtrl);

            bool isCtrl, isShift, isAlt;

            isShift = (ksLShift & KeyStates.Down) > 0 || (ksRShift & KeyStates.Down) > 0;
            isCtrl = (ksLCtrl & KeyStates.Down) > 0 || (ksRCtrl & KeyStates.Down) > 0;
            isAlt = (ksLAlt & KeyStates.Down) > 0 || (ksRAlt & KeyStates.Down) > 0;

            switch (e.Key)
            {
                case Key.Escape:
                    {
                        if (!isCtrl && !isShift && !isAlt)
                        {
                            this.DialogResult = false;
                            return;
                        }
                        break;
                    }
                case Key.Enter:
                    {
                        if (isCtrl)
                        {
                            BtnOk_Click(sender, e);
                        }
                        //else  // 会和题号切换框冲突。
                        //{
                        //    var result = LMessageBox.Show("选择题各部分不支持换行操作。要继续编辑下一题吗？",
                        //        Globals.AppName, MessageBoxButton.YesNo, MessageBoxImage.Question, "JumpToNextChoiceQuestion", null, this);

                        //    if (result != MessageBoxResult.Yes) return;

                        //    btnNxt_Click(sender, e);
                        //    e.Handled = true;
                        //}
                        break;
                    }
                case Key.PageDown:
                    {
                        if (isCtrl || isAlt) break;
                        if (isShift)
                        {
                            btnLst_Click(sender, e);
                        }
                        else
                        {
                            btnNxt_Click(sender, e);
                        }
                        e.Handled = true;
                        break;
                    }
                case Key.PageUp:
                    {
                        if (isCtrl || isAlt) break;
                        if (isShift)
                        {
                            btnFst_Click(sender, e);
                        }
                        else
                        {
                            btnPre_Click(sender, e);
                        }
                        e.Handled = true;
                        break;
                    }
                case Key.D0:
                    {
                        if (isCtrl)
                        {
                            btnToAnswerAnaly_Click(sender, e);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.D1:
                    {
                        if (isCtrl)
                        {
                            btnToError1Analy_Click(sender, e);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.D2:
                    {
                        if (isCtrl)
                        {
                            btnToError2Analy_Click(sender, e);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.D3:
                    {
                        if (isCtrl)
                        {
                            btnToError3Analy_Click(sender, e);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.B:  // Back，上一题
                case Key.P:  // Preview，上一题{
                    {
                        if (isCtrl)
                        {
                            btnPre_Click(sender, e);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.F:  // Forward，后（前进）一题
                case Key.N:  // Next，后一题
                    {
                        if (isCtrl)
                        {
                            btnNxt_Click(sender, e);
                            e.Handled = true;
                        }
                        break;
                    }
                case Key.Q:
                    {
                        if (isCtrl)
                        {
                            mtbNumber.Focus();
                            e.Handled = true;
                        }
                        break;
                    }
            }
        }

        private List<TextBox> anaTbxs = new List<TextBox>();

        private void btnToAnswerAnaly_Click(object sender, RoutedEventArgs e)
        {
            var isCtrl = Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl);
            foreach (var tbx in anaTbxs)
            {
                var selText = tbx.SelectedText;
                selText = selText.Trim(new char[] { ',', '.', '，', '。', ';', '；', '\r', '\n', ' ', '　', '\t', }) + "。";
                if (tbx != atbAnswerAnaly && tbx.IsFocused && selText.Length > 0)
                {
                    atbAnswerAnaly.Select(atbAnswerAnaly.Text.Length, 0);
                    atbAnswerAnaly.SelectedText = selText;
                    atbAnswerAnaly.Select(atbAnswerAnaly.Text.Length, 0);

                    if (isCtrl == false)  // 按住 Ctrl 是复制，不然是移动。
                    {
                        tbx.SelectedText = "";
                        if (tbx.SelectionStart > 0)
                        {
                            var left = tbx.Text.Substring(0, tbx.SelectionStart).TrimEnd(new char[] { ',', '.', ';', '；', ' ', '　', '\t', });
                            if (left.Length < tbx.SelectionStart)
                            {
                                tbx.Select(left.Length, tbx.SelectionStart - left.Length);
                                tbx.SelectedText = "。";
                                tbx.Select(tbx.SelectionStart + 1, 0);
                            }
                        }

                        Regex reg = new Regex(@"[;.；。]{1,}");
                        var match = reg.Match(tbx.Text.Substring(tbx.SelectionStart));
                        if (match.Success && match.Index > 0)
                        {
                            tbx.Select(tbx.SelectionStart, match.Index + match.Length);
                            var cm = tbx.Tag as ContextMenu;
                            if (cm != null)
                                cm.IsOpen = true;
                        }
                    }
                    return;
                }
            }
        }

        private void btnToError1Analy_Click(object sender, RoutedEventArgs e)
        {
            var isCtrl = Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl);
            foreach (var tbx in anaTbxs)
            {
                var selText = tbx.SelectedText;
                selText = selText.Trim(new char[] { ',', '.', '，', '。', ';', '；', '\r', '\n', ' ', '　', '\t', }) + "。";
                if (tbx != atbError1Analy && tbx.IsFocused && selText.Length > 0)
                {
                    atbError1Analy.Select(atbError1Analy.Text.Length, 0);
                    atbError1Analy.SelectedText = selText;
                    atbError1Analy.Select(atbError1Analy.Text.Length, 0);
                    if (isCtrl == false)  // 按住 Ctrl 是复制，不然是移动。
                    {
                        tbx.SelectedText = "";
                        if (tbx.SelectionStart > 0)
                        {
                            var left = tbx.Text.Substring(0, tbx.SelectionStart).TrimEnd(new char[] { ',', '.', ';', '；', ' ', '　', '\t', });
                            if (left.Length < tbx.SelectionStart)
                            {
                                tbx.Select(left.Length, tbx.SelectionStart - left.Length);
                                tbx.SelectedText = "。";
                                tbx.Select(tbx.SelectionStart + 1, 0);
                            }
                        }

                        Regex reg = new Regex(@"[;.；。]{1,}");
                        var match = reg.Match(tbx.Text.Substring(tbx.SelectionStart));
                        if (match.Success && match.Index > 0)
                        {
                            tbx.Select(tbx.SelectionStart, match.Index + match.Length);
                            var cm = tbx.Tag as ContextMenu;
                            if (cm != null)
                                cm.IsOpen = true;
                        }
                    }
                    return;
                }
            }
        }

        private void btnToError2Analy_Click(object sender, RoutedEventArgs e)
        {
            var isCtrl = Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl);
            foreach (var tbx in anaTbxs)
            {
                var selText = tbx.SelectedText;
                selText = selText.Trim(new char[] { ',', '.', '，', '。', ';', '；', '\r', '\n', ' ', '　', '\t', }) + "。";
                if (tbx != atbError2Analy && tbx.IsFocused && selText.Length > 0)
                {
                    atbError2Analy.Select(atbError2Analy.Text.Length, 0);
                    atbError2Analy.SelectedText = selText;
                    atbError2Analy.Select(atbError2Analy.Text.Length, 0);
                    if (isCtrl == false)  // 按住 Ctrl 是复制，不然是移动。
                    {
                        tbx.SelectedText = "";
                        if (tbx.SelectionStart > 0)
                        {
                            var left = tbx.Text.Substring(0, tbx.SelectionStart).TrimEnd(new char[] { ',', '.', ';', '；', ' ', '　', '\t', });
                            if (left.Length < tbx.SelectionStart)
                            {
                                tbx.Select(left.Length, tbx.SelectionStart - left.Length);
                                tbx.SelectedText = "。";
                                tbx.Select(tbx.SelectionStart + 1, 0);
                            }
                        }

                        Regex reg = new Regex(@"[;.；。]{1,}");
                        var match = reg.Match(tbx.Text.Substring(tbx.SelectionStart));
                        if (match.Success && match.Index > 0)
                        {
                            tbx.Select(tbx.SelectionStart, match.Index + match.Length);
                            var cm = tbx.Tag as ContextMenu;
                            if (cm != null)
                                cm.IsOpen = true;
                        }
                    }
                    return;
                }
            }
        }

        private void btnToError3Analy_Click(object sender, RoutedEventArgs e)
        {
            var isCtrl = Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl);
            foreach (var tbx in anaTbxs)
            {
                var selText = tbx.SelectedText;
                selText = selText.Trim(new char[] { ',', '.', '，', '。', ';', '；', '\r', '\n', ' ', '　', '\t', }) + "。";
                if (tbx != atbError3Analy && tbx.IsFocused && selText.Length > 0)
                {
                    atbError3Analy.Select(atbError3Analy.Text.Length, 0);
                    atbError3Analy.SelectedText = selText;
                    atbError3Analy.Select(atbError3Analy.Text.Length, 0);
                    if (isCtrl == false)  // 按住 Ctrl 是复制，不然是移动。
                    {
                        tbx.SelectedText = "";
                        Regex reg = new Regex(@"[;.；。]{1,}");
                        var match = reg.Match(tbx.Text.Substring(tbx.SelectionStart));
                        if (match.Success && match.Index > 0)
                        {
                            tbx.Select(tbx.SelectionStart, match.Index + match.Length);
                            var cm = tbx.Tag as ContextMenu;
                            if (cm != null)
                                cm.IsOpen = true;
                        }

                        if (tbx.SelectionStart > 0)
                        {
                            var left = tbx.Text.Substring(0, tbx.SelectionStart).TrimEnd(new char[] { ',', '.', ';', '；', ' ', '　', '\t', });
                            if (left.Length < tbx.SelectionStart)
                            {
                                tbx.Select(left.Length, tbx.SelectionStart - left.Length);
                                tbx.SelectedText = "。";
                                tbx.Select(tbx.SelectionStart + 1, 0);
                            }
                        }
                    }
                    return;
                }
            }
        }

        private void atbAnswerAnaly_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            var cm = atbAnswerAnaly.Tag as ContextMenu;
            if (cm != null && atbAnswerAnaly.SelectedText.Length > 0)
            {
                cm.IsOpen = true;
                e.Handled = true;
            }
        }

        private void btnDelete_Click(object sender, RoutedEventArgs e)
        {
            if (mtbNumber.Value.HasValue == false) return;

            if (mtbNumber.Value.Value >= 1 && mtbNumber.Value.Value <= questionInfos.Count)
            {
                questionInfos[mtbNumber.Value.Value - 1].ShouldRemove = true;
                tbShouldDelete.Text = "本题将被删除！！！";
                atbTitle.TextDecorations = TextDecorations.Strikethrough;
            }
        }

        private void btnUnDelete_Click(object sender, RoutedEventArgs e)
        {
            if (mtbNumber.Value.HasValue == false) return;

            if (mtbNumber.Value.Value >= 1 && mtbNumber.Value.Value <= questionInfos.Count)
            {
                questionInfos[mtbNumber.Value.Value - 1].ShouldRemove = false;
                tbShouldDelete.Text = "";
                atbTitle.TextDecorations = null;
            }
        }

        private ImagePreviewWindow ipw = null;

        private void btnShowImage_Click(object sender, RoutedEventArgs e)
        {
            if (btnShowImage.Tag == null || System.IO.File.Exists(btnShowImage.Tag as string) == false)
            {
                return;
            }

            ipw = new ImagePreviewWindow(btnShowImage.Tag as string, "")
            {
                Owner = App.Current.MainWindow,
                WindowStartupLocation = System.Windows.WindowStartupLocation.CenterOwner,
                WindowState = System.Windows.WindowState.Normal,
                Height = 480,
                Width = 640,
            };
            ipw.Show();  //允许多张图片同时演示
        }

        private void btnDelToStart_Click(object sender, RoutedEventArgs e)
        {
            KeyStates ksRShift = Keyboard.GetKeyStates(Key.RightShift);
            KeyStates ksLShift = Keyboard.GetKeyStates(Key.LeftShift);
            bool isShift = (ksLShift & KeyStates.Down) > 0 || (ksRShift & KeyStates.Down) > 0;

            if (isShift)
            {
                MiDeleteToStartWithSelectedText_Click(sender, e);
            }
            else
            {
                MiDeleteToStart_Click(sender, e);
            }
        }

        private void btnDelToEnd_Click(object sender, RoutedEventArgs e)
        {
            KeyStates ksRShift = Keyboard.GetKeyStates(Key.RightShift);
            KeyStates ksLShift = Keyboard.GetKeyStates(Key.LeftShift);
            bool isShift = (ksLShift & KeyStates.Down) > 0 || (ksRShift & KeyStates.Down) > 0;

            if (isShift)
            {
                MiDeleteToEndWithSelectedText_Click(sender, e);
            }
            else
            {
                MiDeleteToEnd_Click(sender, e);
            }
        }

        private string replaceDictInitContent = "；这是用于在选择题编辑器中进行替换的字典。\r\n" +
                                 "；在此文档中，以分号开头的行是注释行，会被忽略。\r\n" +
                                 "；空行也会被忽略。\r\n" +
                                 "；查找规则行应写成“查找：xxx”开头的行，其中“xxx”为查找规则。\r\n" +
                                 "；替换规则可以省略，或者写成“替换：yyy”开头的行，其中“yyy”为替换文本。\r\n" +
                                 "；注意：这里的替换和主编辑器的【批量替换】不同！" +
                                 "；这个字典进行查找时【不支持正则表达式】，也【不支持共用替换规则】！\r\n" +
                                 "；如果不写替换规则，视为替换为空字符串。\r\n";

        private string replaceDictSourceText = "";

        /// <summary>
        /// 仅用于此处。
        /// </summary>
        internal class ReplaceRule
        {
            internal string FindRule { get; set; } = "";
            internal string Replacement { get; set; } = "";
        }

        private void btnReplaceAnaly_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
        {
            var path = Globals.PathOfUserFolder + "选择题替换字典.txt";
            if (System.IO.File.Exists(path) == false)
            {
                string content = replaceDictInitContent;
                System.IO.File.WriteAllText(path, content, Encoding.UTF8);
            }

            System.Diagnostics.Process.Start("explorer.exe", "\"" + path + "\"");
        }

        private void btnReplaceAnaly_Click(object sender, RoutedEventArgs e)
        {
            if (string.IsNullOrEmpty(replaceDictSourceText))
            {
                btnReadReplaceAnaly_Click(sender, e);

                if (string.IsNullOrEmpty(replaceDictSourceText))
                    return;
            }

            ReplaceAnalysText();
        }

        private void ReplaceAnalysText()
        {
            var lines = replaceDictSourceText.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);

            var rules = new List<ReplaceRule>();
            var preRule = new ReplaceRule();
            foreach (var line in lines)
            {
                if (line.StartsWith(";") || line.StartsWith("；") || string.IsNullOrEmpty(line)) continue;

                if (line.StartsWith("查找：") || line.StartsWith("查找:"))
                {
                    if (string.IsNullOrEmpty(preRule.FindRule) == false && string.IsNullOrEmpty(preRule.Replacement))
                    {
                        rules.Add(preRule);           // 不写替换规则以空字符串为替换规则。
                        preRule = new ReplaceRule();
                    }

                    var tail = line.Substring(3);
                    if (tail.Length <= 0) continue;

                    preRule.FindRule = tail;
                    continue;
                }

                if (line.StartsWith("替换：") || line.StartsWith("替换:"))
                {
                    if (string.IsNullOrEmpty(preRule.FindRule)) continue;

                    //var tail = line.Substring(3);
                    //if (tail.Length <= 0) continue;  // 这个不能加，否则就没有“替换为空字符串”的功能了。

                    preRule.Replacement = line.Substring(3);  // 不写替换规则视为空，写“替换：”也为空。
                    rules.Add(preRule);
                    preRule = new ReplaceRule();
                    continue;
                }
            }

            if (string.IsNullOrEmpty(preRule.FindRule) == false && string.IsNullOrEmpty(preRule.Replacement))
            {
                rules.Add(preRule);
            }

            var txt = atbAnswerAnaly.Text;
            for (int i = 0; i < rules.Count; i++)
            {
                var rule = rules[i];
                txt = txt.Replace(rule.FindRule, rule.Replacement);
            }

            atbAnswerAnaly.Text = txt;
        }

        private void btnReadReplaceAnaly_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                var path = Globals.PathOfUserFolder + "选择题替换字典.txt";
                if (System.IO.File.Exists(path))
                {
                    replaceDictSourceText = System.IO.File.ReadAllText(path, Encoding.UTF8);
                }
                else
                {
                    replaceDictSourceText = "";
                }
            }
            catch (Exception ex)
            {
                replaceDictSourceText = "";
                LMessageBox.ShowWarning("未能读入替换字典！错误消息如下：\r\n\r\n" + ex.Message);
            }
        }

        private ChoiceCheckpointPopup popupOfAnswerCheckPoints;    //= new ChoiceCheckpointPopup() { Height = 240, Width = 120, };
        private ChoiceCheckpointPopup popupOfBranch01CheckPoint;    // = new ChoiceCheckpointPopup() { Height = 240, Width = 120, };
        private ChoiceCheckpointPopup popupOfBranch02CheckPoint;    // = new ChoiceCheckpointPopup() { Height = 240, Width = 120, };
        private ChoiceCheckpointPopup popupOfBranch03CheckPoint;    // = new ChoiceCheckpointPopup() { Height = 240, Width = 120, };

        /// <summary>
        /// 是否显示每个选项的考查点面板。
        /// </summary>
        private bool isCheckPointPanelRowsVisible = false;

        private void BtnShowCheckPointPanels_Click(object sender, RoutedEventArgs e)
        {
            isCheckPointPanelRowsVisible = !isCheckPointPanelRowsVisible;
            App.WorkspaceConfigManager.Set("CheckPointPanelRowsVisible", isCheckPointPanelRowsVisible.ToString());
            RefreshIsCheckPointPanelRowsVisible();
        }

        private void RefreshIsCheckPointPanelRowsVisible()
        {
            if (isCheckPointPanelRowsVisible)
            {
                rdCheckPointsPanelAnswer.Height =
                    rdCheckPointsPanel1.Height =
                    rdCheckPointsPanel2.Height =
                    rdCheckPointsPanel3.Height =
                    new GridLength(1, GridUnitType.Star);
                BtnShowCheckPointPanels.Background = L.ThemeBrown;
                pShowCheckPointPanels.Fill = Brushes.White;
            }
            else
            {
                rdCheckPointsPanelAnswer.Height =
                    rdCheckPointsPanel1.Height =
                    rdCheckPointsPanel2.Height =
                    rdCheckPointsPanel3.Height =
                    new GridLength(0, GridUnitType.Star);
                BtnShowCheckPointPanels.Background = Brushes.Transparent;
                pShowCheckPointPanels.Fill = Brushes.Black;
            }
        }

        private void btnCheckPointOfAnswer_Click(object sender, RoutedEventArgs e)
        {
            if (popupOfAnswerCheckPoints.IsOpen)
                popupOfAnswerCheckPoints.IsOpen = false;
            if (popupOfAnswerCheckPoints.MasterTextBox == null)
                popupOfAnswerCheckPoints.MasterTextBox = tbCheckPointOfAnswer;
            if (popupOfAnswerCheckPoints.MasterButton == null)
                popupOfAnswerCheckPoints.MasterButton = btnCheckPointOfAnswer;
            popupOfAnswerCheckPoints.IsOpen = true;
        }

        private void btnCheckPointOfBranch01_Click(object sender, RoutedEventArgs e)
        {
            if (popupOfBranch01CheckPoint.IsOpen)
                popupOfBranch01CheckPoint.IsOpen = false;
            if (popupOfBranch01CheckPoint.MasterTextBox == null)
                popupOfBranch01CheckPoint.MasterTextBox = tbCheckPointOfBranch1;
            if (popupOfBranch01CheckPoint.MasterButton == null)
                popupOfBranch01CheckPoint.MasterButton = btnCheckPointOfBranch01;
            popupOfBranch01CheckPoint.IsOpen = true;
        }

        private void btnCheckPointOfBranch02_Click(object sender, RoutedEventArgs e)
        {
            if (popupOfBranch02CheckPoint.IsOpen)
                popupOfBranch02CheckPoint.IsOpen = false;
            if (popupOfBranch02CheckPoint.MasterTextBox == null)
                popupOfBranch02CheckPoint.MasterTextBox = tbCheckPointOfBranch2;
            if (popupOfBranch02CheckPoint.MasterButton == null)
                popupOfBranch02CheckPoint.MasterButton = btnCheckPointOfBranch02;
            popupOfBranch02CheckPoint.IsOpen = true;
        }

        private void btnCheckPointOfBranch03_Click(object sender, RoutedEventArgs e)
        {
            if (popupOfBranch03CheckPoint.IsOpen)
                popupOfBranch03CheckPoint.IsOpen = false;
            if (popupOfBranch03CheckPoint.MasterTextBox == null)
                popupOfBranch03CheckPoint.MasterTextBox = tbCheckPointOfBranch3;
            if (popupOfBranch03CheckPoint.MasterButton == null)
                popupOfBranch03CheckPoint.MasterButton = btnCheckPointOfBranch03;
            popupOfBranch03CheckPoint.IsOpen = true;
        }

        private void btnCheckPointOfAnswer_Initialized(object sender, EventArgs e)
        {
            popupOfAnswerCheckPoints = new ChoiceCheckpointPopup();

            popupOfAnswerCheckPoints.Placement = PlacementMode.Absolute;
            popupOfAnswerCheckPoints.PlacementTarget = btnCheckPointOfAnswer;
            // popupOfAnswerCheckPoints.LostFocus += CheckPointsPopup_LostFocus;  // 没必要这样处理，只需要点击编辑器窗口自动更新数据需要可
        }

        private void CheckPointsPopup_LostFocus(object sender, RoutedEventArgs e)
        {
            (sender as ChoiceCheckpointPopup).UpgradeCheckPoints();
        }

        private void btnCheckPointOfBranch01_Initialized(object sender, EventArgs e)
        {
            popupOfBranch01CheckPoint = new ChoiceCheckpointPopup();

            popupOfBranch01CheckPoint.Placement = PlacementMode.Absolute;
            popupOfBranch01CheckPoint.PlacementTarget = btnCheckPointOfBranch01;
            // popupOfBranch01CheckPoint.LostFocus += CheckPointsPopup_LostFocus;
        }

        private void btnCheckPointOfBranch02_Initialized(object sender, EventArgs e)
        {
            popupOfBranch02CheckPoint = new ChoiceCheckpointPopup();

            popupOfBranch02CheckPoint.Placement = PlacementMode.Absolute;
            popupOfBranch02CheckPoint.PlacementTarget = btnCheckPointOfBranch02;
            // popupOfBranch02CheckPoint.LostFocus += CheckPointsPopup_LostFocus;
        }

        private void btnCheckPointOfBranch03_Initialized(object sender, EventArgs e)
        {
            popupOfBranch03CheckPoint = new ChoiceCheckpointPopup();

            popupOfBranch03CheckPoint.Placement = PlacementMode.Absolute;
            popupOfBranch03CheckPoint.PlacementTarget = btnCheckPointOfBranch03;
            // popupOfBranch03CheckPoint.LostFocus += CheckPointsPopup_LostFocus;
        }

        private void BtnSetDefaultCheckPoints_Click(object sender, RoutedEventArgs e)
        {
            var oldValue = App.WorkspaceConfigManager.Get("DefaultCheckPoints");
            if (string.IsNullOrWhiteSpace(oldValue))
                oldValue = "";
            oldValue = Regex.Replace(oldValue, @"[;；]", "\r\n");
            var result = InputBox.Show(Globals.AppName, "请输入备选的题支考查点：", oldValue, false, "以分号或换行分隔。", false, null, null, null, 220, true);
            if (string.IsNullOrWhiteSpace(result)) return;

            result = result.Replace("\r\n", ";").Replace("；", ";");

            App.WorkspaceConfigManager.Set("DefaultCheckPoints", result);
            ChoiceCheckpointPopup.RefreshDefaultCheckPoints();
            LMessageBox.ShowInfomation("设置成功！");
        }

        private void btnAutoSplit_Click(object sender, RoutedEventArgs e)
        {
            var src = atbAnswerAnaly.Text;
            if (string.IsNullOrEmpty(src))
            {
                LMessageBox.ShowWarning("没找到可以拆分的解析内容！");
                return;
            }

            var reg = new Regex(@".*?((此项正确)|(排除此项)|(此项错误)|(此项排除)|(故选此项))[；。，;\.,]");

            var matches = reg.Matches(src);
            if (matches.Count <= 1)
            {
                LMessageBox.ShowInfomation("题支解析数据不足，或者未找到明显的切分标志字符串！");
                return;
            }

            var trimChars = new char[] { '；', '。', '，', ';', '.', ',' };

            if (matches.Count >= 2)
                atbAnswerAnaly.Text = matches[0].Value.Trim(trimChars) + "。";
            atbError1Analy.Text = matches[1].Value.Trim(trimChars) + "。";

            if (matches.Count >= 3)
                atbError2Analy.Text = matches[2].Value.Trim(trimChars) + "。";

            if (matches.Count >= 4)
                atbError3Analy.Text = matches[3].Value.Trim(trimChars) + "。";
        }

        private void MetroWindow_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            // 使考查点 popup 自动失焦并更新数据
            if(popupOfAnswerCheckPoints!= null && popupOfAnswerCheckPoints.IsOpen)
            {
                popupOfAnswerCheckPoints.UpgradeCheckPoints();
            }

            if (popupOfBranch01CheckPoint != null && popupOfBranch01CheckPoint.IsOpen)
            {
                popupOfBranch01CheckPoint.UpgradeCheckPoints();
            }

            if (popupOfBranch02CheckPoint != null && popupOfBranch02CheckPoint.IsOpen)
            {
                popupOfBranch02CheckPoint.UpgradeCheckPoints();
            }

            if (popupOfBranch03CheckPoint != null && popupOfBranch03CheckPoint.IsOpen)
            {
                popupOfBranch03CheckPoint.UpgradeCheckPoints();
            }
        }
    }

    public class QEdit
    {
        public TextBox ChoiceItemEdit { get; set; }
        public TextBox ChoiceItemAnalyEdit { get; set; }
        public TextBox ChoiceItemCheckPointEdit { get; set; }
    }

    public class QuestionInfo : Question
    {
        public QuestionInfo(Match match, string titleString, string bodyString, bool shouldRandom = false) : base(titleString, bodyString, shouldRandom)
        {
            this.Match = match;
        }
        public Match Match { get; set; }
        public bool ShouldRemove { get; set; } = false;
    }

    public class ChoiceCheckpointPopup : Popup
    {
        public ChoiceCheckpointPopup()
        {
            this.baseBorder.BorderBrush = L.ThemeBrown;
            this.baseBorder.BorderThickness = new Thickness(1);
            this.Child = this.baseBorder;

            this.toolBarPanel.Margin = new Thickness(0, 2, 2, 2);

            this.toolBarPanel.Children.Add(this.btnCancel);
            DockPanel.SetDock(this.btnCancel, Dock.Right);

            this.toolBarPanel.Children.Add(this.btnOk);
            DockPanel.SetDock(this.btnOk, Dock.Right);

            this.basePanel.Children.Add(this.toolBarPanel);
            DockPanel.SetDock(this.toolBarPanel, Dock.Bottom);

            this.itemsPanel.Style = Globals.MainWindow.TryFindResource("MetroListBox") as Style;

            this.basePanel.Children.Add(this.itemsPanel);
            this.baseBorder.Child = this.basePanel;

            this.Opened += ChoiceCheckpointPopup_Opened;

            this.btnCancel.Click += BtnCancel_Click;
            this.btnOk.Click += BtnOk_Click;
        }

        private void BtnOk_Click(object sender, RoutedEventArgs e)
        {
            UpgradeCheckPoints();
        }

        public void UpgradeCheckPoints()
        {
            var text = "";
            foreach (var ue in itemsPanel.Items)
            {
                var ci = ue as CheckBoxItem;
                if (ci == null || ci.IsChecked == false) continue;

                var itemText = ci.Title;
                if (string.IsNullOrWhiteSpace(itemText)) continue;

                text += itemText + "；";
            }
            this.MasterTextBox.Text = text.Trim(new char[] { ';', '；', ' ', '　', '\t' });

            this.IsOpen = false;
        }

        private void BtnCancel_Click(object sender, RoutedEventArgs e)
        {
            this.IsOpen = false;
        }

        private static BitmapImage dialogOK;

        private static BitmapImage dialogCancel;

        static ChoiceCheckpointPopup()
        {
            dialogOK = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/dialogok.png"));
            dialogCancel = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/dialogcancel.png"));
            // 上这些不能放到后面

            RefreshDefaultCheckPoints();
        }

        public static void RefreshDefaultCheckPoints()
        {
            defaultCheckPoints.Clear();

            var txt = App.WorkspaceConfigManager.Get("DefaultCheckPoints");
            if (string.IsNullOrWhiteSpace(txt)) return;

            var pieces = txt.Split(new char[] { ';', '；', }, StringSplitOptions.RemoveEmptyEntries);
            if (pieces.Length <= 0) return;

            foreach (var piece in pieces)
            {
                DefaultCheckPoints.Add(piece);
            }
        }

        private static List<string> defaultCheckPoints = new List<string>();
        /// <summary>
        /// 默认备选的考查点项目列表。
        /// </summary>
        public static List<string> DefaultCheckPoints
        {
            get { return defaultCheckPoints; }
            set { defaultCheckPoints = value; }
        }

        private DockPanel basePanel = new DockPanel() { LastChildFill = true, };
        private DockPanel toolBarPanel = new DockPanel() { LastChildFill = false };
        private ListBox itemsPanel = new ListBox();
        private Button btnOk = new Button()
        {
            Content = new Image() { Source = dialogOK, Width = 16, Height = 16, },
            Height = 24,
            Width = 24,
            Padding = new Thickness(0),
            Margin = new Thickness(0, 0, 0, 0),
            BorderThickness = new Thickness(1,1,0,1),
            Style = Globals.MainWindow.TryFindResource("LDefaultSquareButton") as Style,
        };
        private Button btnCancel = new Button()
        {
            Content = new Image() { Source = dialogCancel, Width = 16, Height = 16, },
            Height = 24,
            Width = 24,
            Padding = new Thickness(0),
            Margin = new Thickness(0, 0, 0, 0),
            BorderThickness = new Thickness(0,1,1,1),
            Style = Globals.MainWindow.TryFindResource("LCancelSquareButton") as Style,
        };

        private void ChoiceCheckpointPopup_Opened(object sender, EventArgs e)
        {
            this.itemsPanel.Items.Clear();

            if (this.MasterTextBox == null)
            {
                this.IsOpen = false;
                return;
            }

            foreach (var cp in defaultCheckPoints)
            {
                var ci = new CheckBoxItem()
                {
                    Title = cp.Trim(),
                    IsChecked = false,
                    Style = FindResource("MetroListBoxItem") as Style,
                };
                // ci.PreviewMouseDown += Ci_PreviewMouseDown;  // 没必要这样处理，只需要点击编辑器窗口自动更新数据需要可
                this.itemsPanel.Items.Add(ci);
            }

            var pieces = Regex.Split(MasterTextBox.Text, "[;；]");
            if (pieces.Length <= 0)
            {
                this.IsOpen = false;
                return;
            }

            foreach (var piece in pieces)
            {
                var pieceText = piece.Trim();
                if (string.IsNullOrWhiteSpace(piece)) continue;

                if (HasItem(piece)) continue;

                var ci = new CheckBoxItem()
                {
                    Title = pieceText.Trim(),
                    IsChecked = true,
                    Style = FindResource("MetroListBoxItem") as Style,
                };

                this.itemsPanel.Items.Add(ci);
            }

            if (this.itemsPanel.Items.Count <= 0)
            {
                this.itemsPanel.Items.Add(new TextBlock() { Text = "无可选考查点！" });
            }

            // 重新定位
            var point = MasterButton.PointToScreen(new Point(0, 0));

            if (Environment.OSVersion.Version.Major > 6 ||
                (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor > 1))  // win 8 以上（6.2）
            {
                HorizontalOffset = point.X;
                VerticalOffset = point.Y + MasterButton.ActualHeight;
            }
            else
            {
                HorizontalOffset = point.X;
                VerticalOffset = point.Y + MasterButton.ActualHeight;
            }
        }

        //private void Ci_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        //{
        //    var ckbox = sender as CheckBoxItem;
        //    if (ckbox == null) return;

        //    if(ckbox.IsChecked == true)
        //    {
        //        ckbox.IsChecked = false;
        //    }
        //    else
        //    {
        //        ckbox.IsChecked = true;
        //    }
        //    e.Handled = true;
        //    // 这是为了解决点击第二个选项时 popup 失焦导致自动更新数据并关闭的问题。
        //    // 但这样做会带来另一个问题：点击编辑器窗口时不会自动导致 popup 失焦——所以还需要在编辑器窗口处理一下。
        //}
        // 没必要这样处理，只需要点击编辑器窗口自动更新数据需要可

        /// <summary>
        /// 是否已存在此項目！
        /// </summary>
        /// <param name="v"></param>
        /// <returns></returns>
        private bool HasItem(string v)
        {
            var txt = v.Trim();

            foreach (var item in this.itemsPanel.Items)
            {
                var ci = item as CheckBoxItem;
                if (ci == null) continue;

                if (ci.Title == txt)
                {
                    ci.IsChecked = true;
                    return true;
                }
            }

            return false;
        }

        public TextBox MasterTextBox { get; set; }

        public Button MasterButton { get; set; }

        private Border baseBorder = new Border() { Background = Brushes.WhiteSmoke, };
    }
}
